feat(i18n): 国际化与本地化基础#2799
Conversation
审阅者指南引入一个启动器范围的本地化系统,使 UI 语言、格式化区域文化以及特定语言的字体配置文件可以在运行时进行配置;将其接入到设置导航、字体选择以及 Minecraft 预运行阶段的语言/字体处理中,并添加测试来验证语言资源和字体配置文件解析的正确性。 从配置应用本地化并更新字体的时序图sequenceDiagram
actor User
participant SettingsUI
participant Config
participant LocalizationService
participant ApplicationResources
participant LocalizationFontService
participant FontSelector
User->>SettingsUI: change UiLanguage or UiFormatCulture
SettingsUI->>Config: update Preference.Localization values
Config-->>LocalizationService: trigger OnLanguageConfigChanged
LocalizationService->>LocalizationService: ApplyFromConfig(save=true)
LocalizationService->>Config: read Localization.Language and FormatCulture
LocalizationService->>LocalizationService: ResolveLanguage(languageCode)
LocalizationService->>LocalizationService: _ResolveFormatCulture(formatCultureCode, uiCulture)
LocalizationService->>LocalizationService: _ApplyCultures(uiCulture, formatCulture)
LocalizationService->>ApplicationResources: _ApplyLanguageResources(languageCode)
LocalizationService->>LocalizationFontService: ApplyLaunchFont(Config.Preference.Font, language)
LocalizationFontService->>ApplicationResources: set LaunchFontFamily
LocalizationService-->>Config: persist normalized Language and FormatCulture
LocalizationService-->>LocalizationService: update CurrentLanguage and CurrentFormatCulture
LocalizationService-->>FontSelector: LanguageChanged event
FontSelector->>FontSelector: RefreshDefaultFont()
FontSelector->>LocalizationFontService: BuildLaunchFontFamily()
FontSelector->>FontSelector: update default CustomFontProperties.Font
Minecraft 预运行语言与字体处理的时序图sequenceDiagram
participant ModLaunch
participant ModMinecraft
participant LocalizationService
participant ModBase
participant McConfigIni as McConfigIni
ModLaunch->>McConfigIni: ReadIni(lang)
McConfigIni-->>ModLaunch: currentLang
ModLaunch->>ModMinecraft: check saves directory
ModMinecraft-->>ModLaunch: hasExistingSaves
ModLaunch->>ModLaunch: isLanguageUnconfigured = (currentLang == none)
ModLaunch->>ModLaunch: shouldUseDefault = isLanguageUnconfigured or !hasExistingSaves
ModLaunch->>ModMinecraft: get ReleaseTime
ModMinecraft-->>ModLaunch: mcReleaseTime
ModLaunch->>ModLaunch: requiredLang = _ResolveMinecraftLanguage(currentLang, shouldUseDefault, mcReleaseTime)
ModLaunch->>ModLaunch: compare currentLang and requiredLang
alt language changed
ModLaunch->>McConfigIni: WriteIni(lang, "-")
ModLaunch->>McConfigIni: WriteIni(lang, requiredLang)
end
ModLaunch->>ModLaunch: if (isLanguageUnconfigured or !hasExistingSaves) and _ShouldEnableForceUnicodeFont()
ModLaunch->>LocalizationService: CurrentLanguage
LocalizationService-->>ModLaunch: LocalizationLanguage with FontProfile
ModLaunch->>ModLaunch: _ShouldEnableForceUnicodeFont() based on FontProfile
alt forceUnicodeFont enabled
ModLaunch->>McConfigIni: WriteIni(forceUnicodeFont, true)
end
新本地化系统(LocalizationService 及相关类型)的类图classDiagram
class LocalizationService {
<<service>>
+const string Auto
+const string FormatCultureFollowLanguage
+const string DefaultLanguageCode
+static LocalizationLanguage CurrentLanguage
+static CultureInfo CurrentFormatCulture
+static IReadOnlyList~LocalizationLanguage~ SupportedLanguages
+static event Action LanguageChanged
+static void ApplyFromConfig(bool save)
+static void Apply(string languageCode, string formatCultureCode, bool save)
+static bool IsLanguageSupported(string languageCode)
+static LocalizationLanguage ResolveLanguage(string languageCode)
-static void _Start()
-static void _ApplyCultures(CultureInfo uiCulture, CultureInfo formatCulture)
-static void _ApplyLanguageResources(string languageCode, CultureInfo uiCulture, CultureInfo formatCulture)
-static void _ApplyLanguageResourcesCore(Application app, string languageCode)
-static CultureInfo _ResolveFormatCulture(string formatCultureCode, CultureInfo uiCulture, out string normalizedCode)
-static LocalizationLanguage _ResolveSystemLanguage()
-static void _SaveConfigIfNeeded(bool save, string normalizedLanguageCode, LocalizationLanguage language, string normalizedFormatCultureCode)
}
class LocalizationLanguage {
+string Code
+string NativeName
+string CultureName
+LocalizationFontProfile FontProfile
}
class LocalizationFontProfile {
<<enum>>
English
SimplifiedChinese
TraditionalChinese
Japanese
Korean
Other
}
class LocalizationFontService {
<<static>>
-const string PclEnglishFont
-Uri _ApplicationPackUri
+FontFamily BuildLaunchFontFamily(string customFontName, LocalizationLanguage language)
+FontFamily BuildRepresentativeFontFamily(LocalizationLanguage language)
+FontFamily BuildRepresentativeFontFamily(LocalizationFontProfile profile)
+void ApplyLaunchFont(string customFontName, LocalizationLanguage language)
+LocalizationFontProfile ResolveProfileFromCultureName(string cultureName)
-IReadOnlyList~string~ _GetDefaultFamilyNames(LocalizationFontProfile profile)
-IReadOnlyList~string~ _GetCustomFamilyNames(string customFontName, LocalizationFontProfile profile)
}
class Lang {
<<static>>
+string Text(string key)
+string Text(string key, object[] args)
+string Date(DateTime value, string format)
+string Number~T~(T value, string format)
-object _LifecycleSafeFindResource(string key)
}
class LocalizationFormatConverter {
+object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
}
class LocalizationConfigGroup {
+string Language
+string FormatCulture
+string Region
}
class ConfigPreferenceLocalization {
+LocalizationConfigGroup Localization
}
class FontSelector {
+ObservableCollection~CustomFontProperties~ CustomFontCollection
+string Tooltip
+string SelectedFontTag
+int SelectedIndex
+event SelectionChangedEventHandler SelectionChanged
+void LoadFonts()
+void RefreshDefaultFont()
+void StartListeningLanguageChanged()
+void StopListeningLanguageChanged()
+void LocalizationService_LanguageChanged()
}
class CustomFontProperties {
+string Name
+FontFamily Font
+string Tag
+event PropertyChangedEventHandler PropertyChanged
-void SetField~T~(T field, T value, string propertyName)
}
class ModBase {
+static void SetLaunchFont(string FontName)
}
LocalizationService --> LocalizationLanguage : uses
LocalizationService --> LocalizationFontService : ApplyLaunchFont
LocalizationService --> LocalizationFontProfile : CurrentLanguage.FontProfile
LocalizationService --> LocalizationConfigGroup : reads
LocalizationFontService --> LocalizationFontProfile : ResolveProfileFromCultureName
LocalizationFontService --> LocalizationLanguage : BuildLaunchFontFamily
Lang --> LocalizationService : uses cultures
LocalizationFormatConverter --> LocalizationService : uses CultureInfo
ConfigPreferenceLocalization o-- LocalizationConfigGroup
FontSelector --> LocalizationFontService : BuildLaunchFontFamily
FontSelector ..> LocalizationService : subscribes LanguageChanged
FontSelector o-- CustomFontProperties
ModBase --> LocalizationFontService : ApplyLaunchFont
ModBase --> LocalizationService : CurrentLanguage
文件级改动
技巧与命令与 Sourcery 交互
自定义你的体验访问你的 控制面板,以便:
获取帮助Original review guide in EnglishReviewer's GuideIntroduce a launcher-wide localization system with runtime-configurable UI language, formatting culture, and language-specific font profiles, wire it into setup navigation, font selection, and Minecraft pre-run language/font handling, and add tests to validate language resources and font profile resolution. Sequence diagram for applying localization from config and updating fontssequenceDiagram
actor User
participant SettingsUI
participant Config
participant LocalizationService
participant ApplicationResources
participant LocalizationFontService
participant FontSelector
User->>SettingsUI: change UiLanguage or UiFormatCulture
SettingsUI->>Config: update Preference.Localization values
Config-->>LocalizationService: trigger OnLanguageConfigChanged
LocalizationService->>LocalizationService: ApplyFromConfig(save=true)
LocalizationService->>Config: read Localization.Language and FormatCulture
LocalizationService->>LocalizationService: ResolveLanguage(languageCode)
LocalizationService->>LocalizationService: _ResolveFormatCulture(formatCultureCode, uiCulture)
LocalizationService->>LocalizationService: _ApplyCultures(uiCulture, formatCulture)
LocalizationService->>ApplicationResources: _ApplyLanguageResources(languageCode)
LocalizationService->>LocalizationFontService: ApplyLaunchFont(Config.Preference.Font, language)
LocalizationFontService->>ApplicationResources: set LaunchFontFamily
LocalizationService-->>Config: persist normalized Language and FormatCulture
LocalizationService-->>LocalizationService: update CurrentLanguage and CurrentFormatCulture
LocalizationService-->>FontSelector: LanguageChanged event
FontSelector->>FontSelector: RefreshDefaultFont()
FontSelector->>LocalizationFontService: BuildLaunchFontFamily()
FontSelector->>FontSelector: update default CustomFontProperties.Font
Sequence diagram for Minecraft pre-run language and font handlingsequenceDiagram
participant ModLaunch
participant ModMinecraft
participant LocalizationService
participant ModBase
participant McConfigIni as McConfigIni
ModLaunch->>McConfigIni: ReadIni(lang)
McConfigIni-->>ModLaunch: currentLang
ModLaunch->>ModMinecraft: check saves directory
ModMinecraft-->>ModLaunch: hasExistingSaves
ModLaunch->>ModLaunch: isLanguageUnconfigured = (currentLang == none)
ModLaunch->>ModLaunch: shouldUseDefault = isLanguageUnconfigured or !hasExistingSaves
ModLaunch->>ModMinecraft: get ReleaseTime
ModMinecraft-->>ModLaunch: mcReleaseTime
ModLaunch->>ModLaunch: requiredLang = _ResolveMinecraftLanguage(currentLang, shouldUseDefault, mcReleaseTime)
ModLaunch->>ModLaunch: compare currentLang and requiredLang
alt language changed
ModLaunch->>McConfigIni: WriteIni(lang, "-")
ModLaunch->>McConfigIni: WriteIni(lang, requiredLang)
end
ModLaunch->>ModLaunch: if (isLanguageUnconfigured or !hasExistingSaves) and _ShouldEnableForceUnicodeFont()
ModLaunch->>LocalizationService: CurrentLanguage
LocalizationService-->>ModLaunch: LocalizationLanguage with FontProfile
ModLaunch->>ModLaunch: _ShouldEnableForceUnicodeFont() based on FontProfile
alt forceUnicodeFont enabled
ModLaunch->>McConfigIni: WriteIni(forceUnicodeFont, true)
end
Class diagram for new localization system (LocalizationService and related types)classDiagram
class LocalizationService {
<<service>>
+const string Auto
+const string FormatCultureFollowLanguage
+const string DefaultLanguageCode
+static LocalizationLanguage CurrentLanguage
+static CultureInfo CurrentFormatCulture
+static IReadOnlyList~LocalizationLanguage~ SupportedLanguages
+static event Action LanguageChanged
+static void ApplyFromConfig(bool save)
+static void Apply(string languageCode, string formatCultureCode, bool save)
+static bool IsLanguageSupported(string languageCode)
+static LocalizationLanguage ResolveLanguage(string languageCode)
-static void _Start()
-static void _ApplyCultures(CultureInfo uiCulture, CultureInfo formatCulture)
-static void _ApplyLanguageResources(string languageCode, CultureInfo uiCulture, CultureInfo formatCulture)
-static void _ApplyLanguageResourcesCore(Application app, string languageCode)
-static CultureInfo _ResolveFormatCulture(string formatCultureCode, CultureInfo uiCulture, out string normalizedCode)
-static LocalizationLanguage _ResolveSystemLanguage()
-static void _SaveConfigIfNeeded(bool save, string normalizedLanguageCode, LocalizationLanguage language, string normalizedFormatCultureCode)
}
class LocalizationLanguage {
+string Code
+string NativeName
+string CultureName
+LocalizationFontProfile FontProfile
}
class LocalizationFontProfile {
<<enum>>
English
SimplifiedChinese
TraditionalChinese
Japanese
Korean
Other
}
class LocalizationFontService {
<<static>>
-const string PclEnglishFont
-Uri _ApplicationPackUri
+FontFamily BuildLaunchFontFamily(string customFontName, LocalizationLanguage language)
+FontFamily BuildRepresentativeFontFamily(LocalizationLanguage language)
+FontFamily BuildRepresentativeFontFamily(LocalizationFontProfile profile)
+void ApplyLaunchFont(string customFontName, LocalizationLanguage language)
+LocalizationFontProfile ResolveProfileFromCultureName(string cultureName)
-IReadOnlyList~string~ _GetDefaultFamilyNames(LocalizationFontProfile profile)
-IReadOnlyList~string~ _GetCustomFamilyNames(string customFontName, LocalizationFontProfile profile)
}
class Lang {
<<static>>
+string Text(string key)
+string Text(string key, object[] args)
+string Date(DateTime value, string format)
+string Number~T~(T value, string format)
-object _LifecycleSafeFindResource(string key)
}
class LocalizationFormatConverter {
+object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
}
class LocalizationConfigGroup {
+string Language
+string FormatCulture
+string Region
}
class ConfigPreferenceLocalization {
+LocalizationConfigGroup Localization
}
class FontSelector {
+ObservableCollection~CustomFontProperties~ CustomFontCollection
+string Tooltip
+string SelectedFontTag
+int SelectedIndex
+event SelectionChangedEventHandler SelectionChanged
+void LoadFonts()
+void RefreshDefaultFont()
+void StartListeningLanguageChanged()
+void StopListeningLanguageChanged()
+void LocalizationService_LanguageChanged()
}
class CustomFontProperties {
+string Name
+FontFamily Font
+string Tag
+event PropertyChangedEventHandler PropertyChanged
-void SetField~T~(T field, T value, string propertyName)
}
class ModBase {
+static void SetLaunchFont(string FontName)
}
LocalizationService --> LocalizationLanguage : uses
LocalizationService --> LocalizationFontService : ApplyLaunchFont
LocalizationService --> LocalizationFontProfile : CurrentLanguage.FontProfile
LocalizationService --> LocalizationConfigGroup : reads
LocalizationFontService --> LocalizationFontProfile : ResolveProfileFromCultureName
LocalizationFontService --> LocalizationLanguage : BuildLaunchFontFamily
Lang --> LocalizationService : uses cultures
LocalizationFormatConverter --> LocalizationService : uses CultureInfo
ConfigPreferenceLocalization o-- LocalizationConfigGroup
FontSelector --> LocalizationFontService : BuildLaunchFontFamily
FontSelector ..> LocalizationService : subscribes LanguageChanged
FontSelector o-- CustomFontProperties
ModBase --> LocalizationFontService : ApplyLaunchFont
ModBase --> LocalizationService : CurrentLanguage
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
还是那个老问题,内置的
本人能想到的有两种解决办法:
下图展示了 PCL English(下)和 Bahnschrift Light(上)的区别。两者极为相似,只有仔细对比观察才能发现区别。因此可以考虑作为替代字体。
Edited: 暂时选择方案一为解决办法,待讨论。 |
This comment was marked as resolved.
This comment was marked as resolved.
- duplicated Reload() - language change short-circuit check
This comment was marked as resolved.
This comment was marked as resolved.
oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
|
那么配套的 Weblate 之类的服务什么时候架呢,还是说已经有了? |
tangge233 那边 Weblate 似乎已经关闭了?目前还没有一个翻译平台,计划暂时考虑以 PR 的方式接收翻译? |
|
如果暂时没有资源架设新的 Weblate 服务的话,建议优先考虑 Crowdin。使用 PR 贡献翻译将来会面临开发者人手不足的问题。 看看暑假能不能买台服务器架包括 Weblate 在内的一些社区基础设施服务? |
Crowdin 并不是免费的,其免费计划有针对字符串和翻译语言的限制。虽说其有针对开源项目相关的免费政策但是我并不觉得 PCL CE 符合所要求的情况?当初在为 PCL Language 选择翻译平台的时候就因为 PCL 本身不符合 Crowdin 开源项目的要求,且无法负担 Crowdin 订阅的费用选择了自建 Weblate。 以个人的使用体验来说 Crowdin 的体验要好过于 Weblate,前提是有 Crowdin()。可以先考虑尝试申请一下(需要项目主要负责人),如果通过就可以使用 Crowdin 作为翻译平台,否则就得考虑自行搭建 Weblate。 目前还没有展开对现有字符串的迁移工作,所以说暂时还不是一个迫在眉睫的问题,在迁移完成后再考虑也是没有问题的。 |
# Conflicts: # Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.cs
|
再 approve 一下( |








实现 i18n 与 l10n 的基础能力。
待办:
管理 -> 辅助功能中的游戏语言以跟随启动器语言Summary by Sourcery
为启动器引入一个全局本地化系统,用于 UI 语言和格式化区域设置,将其集成到设置中,并与 Minecraft 启动行为和字体处理进行连接。
New Features:
Enhancements:
Tests:
Original summary in English
Summary by Sourcery
Introduce a launcher-wide localization system for UI language and formatting culture, integrate it into settings, and connect it with Minecraft launch behavior and font handling.
New Features:
Enhancements:
Tests: